#define debug(...) cout << #__VA_ARGS__ << " " << (__VA_ARGS__) << endl;
template <class T>
using iterator_t = decay_t<decltype(begin(declval<T>()))>;
template <class T>
using sentinel_t = decay_t<decltype(end(declval<T>()))>;
template <class T, class = void>
inline constexpr bool is_range = false;
template <class T>
inline constexpr bool is_range<T, void_t<iterator_t<T>, sentinel_t<T>>> = true;
template <class I>
using iter_value_t = typename iterator_traits<I>::value_type;
template <class R>
using range_value_t = iter_value_t<iterator_t<R>>;

template <class T, class... Args>
inline constexpr bool is_any_of = (is_same_v<T, Args> || ...);
template <class T, class = void>
inline constexpr bool is_tuple_like = false;
template <class T>
inline constexpr bool
	is_tuple_like<T, void_t<decltype(tuple_size<T> {})>> = true;

template <class T>
inline constexpr bool tuple_like = is_tuple_like<remove_reference_t<T>>;
template <class T>
decltype(auto) _fmt(T&& t)
{
	if constexpr (is_convertible_v<T, string_view>)
		return quoted(string_view(t));
	else if constexpr (is_same_v<decay_t<T>, char>)
		return quoted(string_view { &t, 1 }, '\'');
	else
		return static_cast<T&&>(t);
}
template <class Ch, class Tr, class R,
	enable_if_t<is_range<R> && !is_tuple_like<R> && !is_any_of<range_value_t<R>, char, wchar_t, char16_t, char32_t>, int> = 0>
std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& os, R&& r);
template <class Ch, class Tr, class Tuple, class = void_t<typename tuple_size<Tuple>::type>>
basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& os, Tuple const& t);
template <class Ch, class Tr, class Tuple, std::size_t... Is>
void print_tuple_impl(basic_ostream<Ch, Tr>& os, Tuple const& t, index_sequence<Is...>)
{
	((os << (Is == 0 ? "" : ", ") << _fmt(get<Is>(t))), ...);
}

template <class Ch, class Tr, class Tuple, class>
basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& os, Tuple const& t)
{
	constexpr auto sz = tuple_size_v<remove_reference_t<Tuple>>;
	os << "[";
	print_tuple_impl(os, t, std::make_index_sequence<sz> {});
	return os << "]";
}
template <class Ch, class Tr, class R,
	enable_if_t<is_range<R> && !is_tuple_like<R> && !is_any_of<range_value_t<R>, char, wchar_t, char16_t, char32_t>, int>>
std::basic_ostream<Ch, Tr>& operator<<(std::basic_ostream<Ch, Tr>& os, R&& r)
{
	os << "[";
	size_t i = 0;
	for (auto&& elem : r)
		cout << (++i == 1 ? "" : ", ") << _fmt(elem);
	return os << "]";
}
